package represent.type;

import utils.Lib;

public class DecimalType implements Type {

	private Double val;

	public DecimalType(String s) {
		val = new Double(s);
	}

	public DecimalType(double d) {
		val = d;
	}

	public DecimalType(Type v) {
		Object jval = v.asJavaVal();
		if(v instanceof IntType)
			val = ((Integer)jval).doubleValue();
		else if(v instanceof FloatType)
			val = ((Float) jval).doubleValue();
		else if(v instanceof DecimalType)
			val = (Double) jval;
		else
			throw new Error("Cannot convert " + jval + " to float.");
	}

	public DecimalType(FloatType f) {
		val = ((Float) f.asJavaVal()).doubleValue();
	}

	public DecimalType(IntType i) {
		val = ((Integer) i.asJavaVal()).doubleValue();
	}

	public Object asJavaVal() {
		return val;
	}

	public Type getDecFromString(String s) {
		Integer i = null;
		Double f = null;
		try {
			i = new Integer(s);
		} catch(Exception e1) {
			try {
				f = new Double(s);
			} catch(Exception e2) {
				throw new Error("Cannot conver " + s + " to number");
			}
		}
		if(i != null)
			return new IntType(i);
		else
			return new DecimalType(f);
	}

	public void format(int len, int dec_len) {
		String s = val.toString();
		String parts[] = s.split("\\.");
		String rst = "";
		if(parts.length == 1) {
			rst += parts[0] + ".";
			for(int i = 0; i < dec_len; ++i)
				rst += "0";
		}
		else {
			rst += parts[0] + ".";
			if(parts[1].length() <= dec_len)
				rst += parts[1];
			else {
				Integer dec = new Integer(parts[1]);
				dec /= (int) Math.pow(10, parts[1].length() - dec_len - 1);
				if(dec % 10 >= 5)
					dec += 10;
				dec /= 10;
				rst += dec;
			}
		}
		val = new Double(rst);
	}

	public Type divide(Type c) {
		if (c instanceof StringType) {
			String v = ((StringType) c).asJavaVal();
			return divide(getDecFromString(v));
		} else if(c instanceof IntType) {
			DecimalType d = new DecimalType((IntType) c);
			return new DecimalType(val / (Double)d.asJavaVal());
		}
		else if(c instanceof FloatType) {
			DecimalType d = new DecimalType((FloatType) c);
			return new DecimalType(val / (Double)d.asJavaVal());
		}
		else if(c instanceof DecimalType)
			return new DecimalType(val / (Double)c.asJavaVal());
		else
			throw new Error("Inconsistent types.");
	}

	public String getType() {
		return "decimal";
	}

	public Type minus(Type c) {
		if (c instanceof StringType) {
			String v = ((StringType) c).asJavaVal();
			return minus(getDecFromString(v));
		} else if(c instanceof IntType) {
			DecimalType d = new DecimalType((IntType) c);
			return new DecimalType(val - (Double)d.asJavaVal());
		}
		else if(c instanceof FloatType) {
			DecimalType d = new DecimalType((FloatType) c);
			return new DecimalType(val - (Double)d.asJavaVal());
		}
		else if(c instanceof DecimalType)
			return new DecimalType(val - (Double)c.asJavaVal());
		else
			throw new Error("Inconsistent types.");
	}

	public Type mod(Type c) {
		Lib.assertNotReached();
//		throw new Error("Type decimal should not do mod.");
		return null;
	}

	public Type mult(Type c) {
		if (c instanceof StringType) {
			String v = ((StringType) c).asJavaVal();
			return mult(getDecFromString(v));
		} else if(c instanceof IntType) {
			DecimalType d = new DecimalType((IntType) c);
			return new DecimalType(val * (Double)d.asJavaVal());
		}
		else if(c instanceof FloatType) {
			DecimalType d = new DecimalType((FloatType) c);
			return new DecimalType(val * (Double)d.asJavaVal());
		}
		else if(c instanceof DecimalType)
			return new DecimalType(val * (Double)c.asJavaVal());
		else
			throw new Error("Inconsistent types.");
	}

	public Type plus(Type c) {
		if (c instanceof StringType) {
			String v = ((StringType) c).asJavaVal();
			return plus(getDecFromString(v));
		} else if(c instanceof IntType) {
			DecimalType d = new DecimalType((IntType) c);
			return new DecimalType(val + (Double)d.asJavaVal());
		}
		else if(c instanceof FloatType) {
			DecimalType d = new DecimalType((FloatType) c);
			return new DecimalType(val + (Double)d.asJavaVal());
		}
		else if(c instanceof DecimalType)
			return new DecimalType(val + (Double)c.asJavaVal());
		else
			throw new Error("Inconsistent types.");
	}

	public int compareTo(Type c) {
		DecimalType dv = null;
		if (c instanceof StringType) {
			String v = ((StringType) c).asJavaVal();
			return compareTo(getDecFromString(v));
		} else if(c instanceof IntType)
			dv = new DecimalType((IntType) c);
		else if(c instanceof FloatType)
			dv = new DecimalType((FloatType) c);
		else if(c instanceof DecimalType)
			dv = (DecimalType) c;
		else
			throw new Error("Cannot convert type.");
		return val.compareTo((Double) dv.asJavaVal());
	}

	public boolean equals(Object o) {
		DecimalType dv = null;
		if (o instanceof StringType) {
			String v = (String)(((StringType) o).asJavaVal());
			return equals(getDecFromString(v));
		} else if(o instanceof IntType)
			dv = new DecimalType((IntType) o);
		else if(o instanceof FloatType)
			dv = new DecimalType((FloatType) o);
		else if(o instanceof DecimalType)
			dv = (DecimalType) o;
		else
			throw new Error("Cannot convert type.");
		return val.equals((Double) dv.asJavaVal());
	}

	public static int compare(byte[] a, byte[] b) {
		// TODO �Զ���ɷ������
		return 0;
	}

	public static DecimalType fromBytes(byte[] bytes) {
		// TODO �Զ���ɷ������
		return null;
	}

	public int TypeToInt() {
		return utils.Constant.DECIMAL;
	}
	
	public static int length() {
		return 6;	// 4(M) + 2(D)
	}
}
